package com.joinway.framework.support.logging;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import com.joinway.framework.bean.logging.annotation.AccessLog;
import com.joinway.framework.bean.logging.annotation.AccessLogIgnore;
import com.joinway.framework.bean.logging.annotation.AccessLogKey;
import com.joinway.framework.core.utils.AopHelper;
import com.joinway.framework.core.utils.AppEnv;
import com.joinway.framework.core.utils.FrameworkHelper;
import com.joinway.framework.support.audit.constant.AuditConstants;
import com.joinway.framework.support.logging.service.ILogService;
import com.joinway.framework.support.utils.InterceptorHelper;

public class AccessLogAspect {

	private final static Logger log = LoggerFactory.getLogger(AccessLogAspect.class);
	
	protected ILogService accessLogService;
	
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
        Object output = null;
        
        long start = System.currentTimeMillis();
        Throwable tt = null;
        
        try{
        	output = point.proceed();
        }catch(Throwable t){
        	tt = t;
        	throw t;
        }finally{
        	long end = System.currentTimeMillis();
        	
        	int cost = (int)(end - start);
        	Method method = AopHelper.getMethod(point);
        	AccessLog ann = method.getAnnotation(AccessLog.class);
        	if(ann != null && accessLogService != null){
        		saveLog(point, output, cost, new Date(), tt);
        	}
        }

        return output;
    }
	
	protected void saveLog(ProceedingJoinPoint point, Object output, int cost, Date today, Throwable t){
		Map<String, Object> row = new LinkedHashMap<>();
		
		try{
			Method method = AopHelper.getMethod(point);
	       	AccessLog ann = method.getAnnotation(AccessLog.class);
	       	
	       	if(StringUtils.isNotBlank(ann.value())){
	       		row.put("action_name", ann.value());
	       	}
			row.put("method", point.getTarget().getClass().getCanonicalName() + "::" + method.getName());
			row.put("cost", cost);
			
			Object[] args = point.getArgs();
			if(args != null){
				Map<String, Object> params = new LinkedHashMap<>();
				
				for(int i = 0; i < args.length; i++){
					Annotation[][] anns = method.getParameterAnnotations();
					if(anns != null){
//						AccessLogIgnore ignore = InterceptorHelper.getParameterAnnotation(anns[i], AccessLogIgnore.class);
//						if(ignore != null){
//							continue;
//						}
						
						AccessLogKey key = InterceptorHelper.getParameterAnnotation(anns[i], AccessLogKey.class);
						if(key != null && args[i] != null){
							String actionKey = String.valueOf(args[i]);
							if(StringUtils.isNotBlank(actionKey)){
								row.put(key.value(), actionKey);
							}
						}
					}
					
					params.put("arg" + (i + 1), String.valueOf(args[i]));
				}
				
				row.put("input", String.valueOf(params));
			}
			
			if(output != null){
				row.put("output", String.valueOf(output));
			}
			
			if(t != null){
				row.put("error_message", t.getMessage());
				
				StringWriter sw = new StringWriter();
				t.printStackTrace(new PrintWriter(sw));
				row.put("error_trace", sw.toString());
			}
			
			row.put("access_time", DateFormatUtils.format(today, "yyyyMMddHHmmss"));
			row.put("create_date", today);
			
			Integer status = 0;
			if(t != null){
				status = 1;
				row.put("status", status);
			}
			
			accessLogService.save(row);
		}catch(Throwable tt){
			log.error("failed to insert access log", tt);
		}
	}

	public ILogService getAccessLogService() {
		return accessLogService;
	}

	public void setAccessLogService(ILogService accessLogService) {
		this.accessLogService = accessLogService;
	}
}

